import {
  getAppSupportCommands,
  getAllSetting,
  getStatus,
  getEvents,
  setSetting,
  setSettingBySettings,
  setCommand,
  getApplianceDetail,
  type ApplianceStatus,
  getFeatures,
} from '@/api/appliance'
import type { Base } from './types/base'
import type { Remote } from './types/remote'
import type { WS } from '@/utils/socket/types'
import { debounce } from 'lodash'
import { useWebsocketListener, type WebsocketHandlerImp } from '@/utils/socket/WebSocketHandlerImp'
import { handleStatus } from '@/handle/appliance'
import { actionDebounce } from '@/utils/mpHelper'
import type { BaseController } from '../BaseAppliance/definition'
import type { Response } from '@/utils/request'

// 监听消息防抖延迟时间
const ObserveDebounceDelay = 200

/**
 * 远程API控制
 */
export enum RemoteTypes {
  Inhouse,
  Oem,
}
export class ApplianceRemote<Store extends Base.FullStore> implements Remote.ApplianceRemote<Store> {
  store!: Store
  remoteType: RemoteTypes = RemoteTypes.Inhouse

  observeMap: Remote._ObserveMap<Store> = new Map()
  batchObserves: Set<Remote.BatchObserve<Store>> = new Set()

  websocket!: WebsocketHandlerImp

  constructor(store: Store) {
    this.store = store
  }
  init() {
    // Reset
    this.observeMap.clear()
    this.batchObserves.clear()
  }
  setRemoteType(remoteType: RemoteTypes) {
    this.remoteType = remoteType
  }
  addObserve(observe: Remote.Observe<Store>) {
    Object.entries(observe).forEach(([key, handler]) => {
      let observeSet = this.observeMap.get(key)
      if (!observeSet) {
        observeSet = new Set()
      }
      observeSet.add(debounce(handler, ObserveDebounceDelay))
      this.observeMap.set(key, observeSet)
    })
  }
  addBatchObserve(batchObserve: Remote.BatchObserve<Store>) {
    this.batchObserves.add(batchObserve)
  }
  addBatchObserves(batchObserves: Remote.BatchObserve<Store>[]) {
    batchObserves.forEach((observe) => this.addBatchObserve(observe))
  }
  removeBatchObserve(batchObserve: Remote.BatchObserve<Store>) {
    this.batchObserves.delete(batchObserve)
  }
  removeBatchObserves(batchObserves: Remote.BatchObserve<Store>[]) {
    batchObserves.forEach((observe) => this.removeBatchObserve(observe))
  }

  useMessageListener(websocket: WebsocketHandlerImp) {
    this.websocket = websocket
    useWebsocketListener(websocket, (msg: WS.Message) => {
      // 不存在haId 或 haId不匹配
      if (!this.store._datasource.haId || msg.haId !== this.store._datasource.haId) return

      // 遍历消息 触发监听
      this.handleObserves(msg.items || [], { from: 'message' }, msg)
    })
  }

  /**
   * 加载方法
   */
  loadApplianceDetail = () => this._loadApplianceDetailDebounced()
  private _loadApplianceDetailDebounced = actionDebounce({
    call: async () => {
      const res = await getApplianceDetail(this.store._datasource.haId)
      const detail = await handleStatus(res.data)
      this.store._datasource.detail = detail
    },
  })

  loadSupportCommands = () => this._loadSupportCommandsDebounced()
  private _loadSupportCommandsDebounced = actionDebounce({
    call: async () => {
      const res = await getAppSupportCommands(this.store._datasource.haId)
      this.store._datasource.commands = res.data.commands
    },
  })

  loadAllStatus = () => this._loadAllStatusDebounced()
  private _loadAllStatusDebounced = actionDebounce({
    call: async () => {
      const res = await getStatus(this.store._datasource.haId)
      this.store._datasource.status = res.data.status
      // Status刷新后 触发事件
      this.handleObserves(res.data.status as WS.MessageItem[], { from: 'status' })
    },
  })

  loadAllSetting = () => this._loadAllSettingDebounced()
  private _loadAllSettingDebounced = actionDebounce({
    call: async () => {
      const res = await getAllSetting(this.store._datasource.haId)
      this.store._datasource.settings = res.data.settings
      // Setting刷新后 触发事件
      this.handleObserves(res.data.settings as WS.MessageItem[], { from: 'setting' })
    },
  })

  loadAllEvents = async () => await this._loadAllEventsDebounced()
  private _loadAllEventsDebounced = actionDebounce({
    call: async () => {
      this.store._datasource.events = []
      const res = await getEvents(this.store._datasource.haId)
      this.store._datasource.events = res.data.events
      this.handleObserves(res.data.events, { from: 'event' })
    },
  })

  loadFeatures = async () => await this._loadFeaturesDebounced()
  private _loadFeaturesDebounced = actionDebounce({
    call: async () => {
      const res = await getFeatures(this.store._datasource.haId)
      const { commands, programs, settings, status } = res.data
      this.store._datasource.settings = settings || []
      this.store._datasource.status = status || []
      this.store._datasource.commands = commands || []
      if ((this.store as BaseController.Store).allPrograms !== void 0) {
        ;(this.store as BaseController.Store).allPrograms = programs || null
      }
    },
  })

  /**
   * 设置方法
   */
  async setSetting(key: string, value: string, options?: { ignoreError?: boolean }): Promise<Response<null>> {
    return setSetting(this.store._datasource.haId, key, value, { ignoreError: options?.ignoreError })
  }
  async setSettingBySettings(key: string, value: string): Promise<Response<null>> {
    return setSettingBySettings(this.store._datasource.haId, key, value)
  }
  async setCommand(key: string, value: string): Promise<Response<null>> {
    return setCommand(this.store._datasource.haId, key, value)
  }

  /**
   * 处理events通知配置
   * @param messages
   */
  private handleObserves(
    messages: WS.MessageItem[] | ApplianceStatus[],
    options: { from: 'message' | 'event' | 'status' | 'setting' },
    msgContext?: WS.Message
  ) {
    // 在线离线等 无items 的特殊处理
    if (messages.length === 0) {
      for (const batchObserve of this.batchObserves) {
        if (batchObserve.acceptNullMessage) {
          batchObserve.handler.call(this, null as any, this, msgContext)
        }
      }
    }
    for (const message of messages) {
      const handlers: Set<Remote.ObserveHandler<Store>> = new Set()

      // 非status调用handle方法 处理监听
      const observeHandles = this.observeMap.get(message.key)
      if (observeHandles && ['message', 'event'].includes(options.from)) {
        observeHandles.forEach((handle) => handlers.add(handle))
      }

      // 追加匹配批量监听
      this.batchObserves.forEach((batchObserve) => {
        // 由status调用handle方法 且 批量监听不允许status调用 过滤该监听
        if (options.from === 'status' && !batchObserve.acceptTriggerByStatus) return
        // 由setting调用handle方法 且 批量监听不允许setting调用 过滤该监听
        if (options.from === 'setting' && !batchObserve.acceptTriggerBySetting) return

        let isMatch = false
        // 无matcher 默认true
        if (!batchObserve.matcher) {
          isMatch = true
        }
        // 调用matcher判断
        else {
          try {
            const matchResult = batchObserve.matcher.call(this, message as WS.MessageItem, this, msgContext)
            if (typeof matchResult === 'boolean') {
              isMatch = matchResult
            }
          } catch (e) {
            console.error(`Batch observe error: `, e)
            isMatch = false
          }
        }

        if (!isMatch) return

        handlers.add(batchObserve.handler)
      })

      for (const handler of handlers) {
        try {
          handler.call(this, message as WS.MessageItem, this, msgContext)
        } catch (e) {
          console.error('Remote Handler called by error: ', e)
        }
      }
    }
  }
}
